#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <getopt.h>
#include <dirent.h>
#include <sys/ioctl.h>
#include <ctype.h>
#include <pwd.h>
#include <grp.h>
#include <libgen.h>
#include <time.h>

int mycmp(const void* a, const void* b){
    char ta[256];
    char tb[256];
    strncpy(ta,(*((struct dirent**)a))->d_name, 256);
    strncpy(tb,(*((struct dirent**)b))->d_name, 256);
    int i = 0;
    while(ta[i] != '\0'){
        ta[i] = tolower(ta[i]);
        i++;
    }
    i = 0;
    while(tb[i] != '\0'){
        tb[i] = tolower(tb[i]);
        i++;
    }
    return strcmp(ta, tb);
}

void format_print(char* name){
    struct stat file;
    stat(name, &file);
    //权限位输出
    char buf[12] = "---------- ";
    if(S_ISDIR(file.st_mode)){
        buf[0] = 'd';
    }
    else if(S_ISLNK(file.st_mode)){
        buf[0] = 'l';
    }
    else if(S_ISFIFO(file.st_mode)){
        buf[0] = 'f';
    }
    else if(S_ISBLK(file.st_mode)){
        buf[0] = 'b';
    }
    else if(S_ISCHR(file.st_mode)){
        buf[0] = 'c';
    }
    else if(S_ISDIR(file.st_mode)){
        buf[0] = 'd';
    }
    else if(S_ISSOCK(file.st_mode)){
        buf[0] = 's';
    }

    if(file.st_mode & S_IRUSR){
        buf[1] = 'r';
    }
    if(file.st_mode & S_IWUSR){
        buf[2] = 'w';
    }
    if(file.st_mode & S_IXUSR){
        buf[3] = 'x';
    }
    if(file.st_mode & S_IRGRP){
        buf[4] = 'r';
    }
    if(file.st_mode & S_IWGRP){
        buf[5] = 'w';
    }
    if(file.st_mode & S_IXGRP){
        buf[6] = 'x';
    }
    if(file.st_mode & S_IROTH){
        buf[7] = 'r';
    }
    if(file.st_mode & S_IWOTH){
        buf[8] = 'w';
    }
    if(file.st_mode & S_IXOTH){
        buf[9] = 'x';
    }
    printf("%s", buf);

    //链接数输出
    printf("%ld  ", file.st_nlink);
    //用户
    struct passwd* user = getpwuid(file.st_uid);
    printf("%s  ", user->pw_name);
    //组
    struct group* gp = getgrgid(file.st_gid);
    printf("%s  ",gp->gr_name);
    //大小
    printf("%ld  ", file.st_size);
    //月 日 时 分
    struct tm* time_buf = localtime(&(file.st_atime));
    printf("%d %d %d:%d  ", time_buf->tm_mon + 1, time_buf->tm_mday, time_buf->tm_hour, time_buf->tm_min);


    //文件名
    printf("%s\n", basename(name));
}

void ordinary_show(struct dirent** buf, int total, int a_flag){
    for(int i = 0; i < total; i++){
        if(!a_flag && buf[i]->d_name[0] == '.'){
            continue;
        }
        printf("%s\n", (buf[i]->d_name));
    } 
}

void line_show(char* path, struct dirent** buf, int total, int a_flag){
    for(int i = 0; i < total; i++){
        if(!a_flag && buf[i]->d_name[0] == '.'){
            continue;
        }
        char r_name[1024];
        memset(r_name, '\0', 1024);
        strncpy(r_name, path, strlen(path));
        strncat(r_name, "/", 1);
        strncat(r_name, buf[i]->d_name, strlen(buf[i]->d_name));
        format_print(r_name);
    }
}

void do_ls(char* name, int a_flag, int l_flag){
    struct stat buf;
    if(stat(name, &buf) < 0){
        perror("stat()");
    }
    if(S_ISDIR(buf.st_mode)){
        struct dirent* all_file[1024];
        DIR* cur_dir;
        int total = 0;
        if((cur_dir = opendir(name)) == NULL){
            perror("opendir()");
        }
        struct dirent* temp;
        while((temp = readdir(cur_dir)) != NULL){
            all_file[total] = temp;
            total++;
        }


        // for(int i = 0; i < total; i++){
        //     printf("%s\n", all_file[i]->d_name);
        // }
        qsort(all_file, total, sizeof(struct dirent*), mycmp);
        // for(int i = 0; i < total; i++){
        //     printf("%s\n", all_file[i]->d_name);
        // }


        if(l_flag){
            line_show(name, all_file, total, a_flag);
        }
        else{
            ordinary_show(all_file, total, a_flag);
        }
    }
    else{
        if(l_flag){
            format_print(name);
        }
        else{
            printf("%s\n", name);
        }
    }
}

int main(int argc, char** argv){
    int l_flag = 0;
    int a_flag = 0;
    int opt;
    while((opt = getopt(argc, argv, "al")) != -1){
        switch(opt){
            case 'a':
                a_flag = 1;
                break;
            case 'l':
                l_flag = 1;
                break;
            default:
                fprintf(stderr, "unknown cases\n");
                break;
        }
    }

    if(optind == argc){
        do_ls(".", a_flag, l_flag);
    }
    else if(optind == argc - 1){
        do_ls(argv[optind], a_flag, l_flag);
    }
    else{
        while(optind < argc){
            struct stat buf;
            if(stat(argv[optind], &buf) < 0){
                perror("stat()");
                continue;
            }
            if(S_ISDIR(buf.st_mode)){
                printf("%s :\n", argv[optind]);
            }
            do_ls(argv[optind], a_flag, l_flag);
            optind++;
            printf("\n");
        }
    }

    return 0;
}